#ifndef __CPIL_SHAREDARRAY_HPP__
#define __CPIL_SHAREDARRAY_HPP__

//#define __DEBUG_MODE__

#include "exception.hpp"
#include "memory.hpp"

#ifdef __DEBUG_MODE__
    #include <iostream>
#endif

template< typename DataType >
class ConstIterator;
template< typename DataType >
class Iterator;


template< typename DataType >
class SharedArray
{

friend class ConstIterator<DataType>;
friend class      Iterator<DataType>;

    protected:

        Counter      * cptr;
        DataType     * data;
        unsigned int * size;

    protected:


        inline void deleteObject()
        {
            Memory::getInstance()->delCounter(cptr);
            Memory::getInstance()->delArray(size);
            Memory::getInstance()->delArray(data);
        }

        inline void allocObject(unsigned int _size)
        {
            cptr  = Memory::getInstance()->newCounter();
            size  = Memory::getInstance()->newArray<unsigned int>(1);(*size) = _size;
            data  = Memory::getInstance()->newArray<DataType>(_size);
        }

        inline void copyObject(DataType * src, unsigned int _size)
        {
            allocObject(_size);
            DataType * dst(data);
            for(unsigned int n=0; n < _size ; n++)
                *(dst++) = *(src++);

        }

        inline void attachObject(const SharedArray &sp)
        {
            cptr = sp.cptr;
            size = sp.size;
            data = sp.data;
            (*cptr)++;
        }

        inline void detachObject()
        {
            (*cptr)--;
            if( (*cptr) == 0 )
                deleteObject();
            cptr = 0;
            size = 0;
            data = 0;
        }

        inline void unsharObject()
        {
            #ifdef __DEBUG_MODE__
                std::cout << "SharedArray::unsharObject()" << std::endl;
            #endif
            if( (*cptr) > 1 )
            {
                (*cptr)--;
                copyObject(data,*size);
            }
        }

    public:

        //TODO: comprendre le pb d'auto refernece quand SharedArray t(t);
        SharedArray() : cptr(0), data(0), size(0)
        {
            allocObject(0);
            #ifdef __DEBUG_MODE__
                std::cout <<"["<<this<<"] "<<"SharedArray() ptr ["<<cptr<<"] = ("<<*cptr<<") new cptr"<< std::endl;
            #endif
        }

        virtual ~SharedArray()
        {
            #ifdef __DEBUG_MODE__
                std::cout <<"["<<this<<"] "<<"~SharedArray() ptr ["<<cptr<<"] = ("<<*cptr<<")"<< std::endl;
            #endif
            detachObject();
        }

        SharedArray(const SharedArray &sp) : cptr(0), data(0), size(0)
        {
            attachObject(sp);
            #ifdef __DEBUG_MODE__
                std::cout <<"["<<this<<"] "<<"SharedArray(const SharedArray &) ptr ["<<cptr<<"] = ("<<*cptr<<")"<< std::endl;
            #endif

        }

        virtual SharedArray& operator=(const SharedArray &sp)
        {
            detachObject();
            attachObject(sp);
            #ifdef __DEBUG_MODE__
                std::cout <<"["<<this<<"] "<<"SharedArray& operator=(const SharedArray &) ptr ["<<cptr<<"] = ("<<*cptr<<")"<< std::endl;
            #endif

            return (*this);
        }

        SharedArray(unsigned int _size) : cptr(0), data(0), size(0)
        {
            allocObject(_size);
            #ifdef __DEBUG_MODE__
                std::cout <<"["<<this<<"] "<<"SharedArray(unsigned int) ptr ["<<cptr<<"] = ("<<*cptr<<") new cptr"<< std::endl;
            #endif
        }

        inline unsigned int getCptr() const { return *cptr; }
        inline unsigned int getSize() const { return *size; }
        inline bool        isShared() const { return ((*cptr) > 1 ); }

        void setSize(unsigned int _size)
        {
            if( *size != _size )
            {
                detachObject();
                allocObject(_size);
            }
        }

        SharedArray operator+(const SharedArray &sa)
        {
            if( sa.getSize() != getSize() )
                throw Exception("Vectors must have the same size!",EXCEPTION_INFOS);

            SharedArray res(this->getSize());
            DataType * src1(sa.data), * src2(data), *dst(res.getData());

            for(unsigned int n=0 ; n < getSize() ; n++)
                *(dst++) = *(src1++) + *(src2++);

            return res;
        }

        /*T& operator[](unsigned int pos)
        {
            //std::cout<<"[] write!!!"<<std::endl;
            unsharObject();
            return *(data+pos);
        }

        T operator[](unsigned int pos) const
        {
            //std::cout<<"[] const"<<std::endl;
            return *(data+pos);
        }*/


};
#endif

